package com.xywy.common.util;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.HashMap;

/**
 * 软引用的集合
 *
 * @param <K>   KEY
 * @param <V>   VALUE
 * @author Joker
 */
public class SoftReferenceMap<K, V> extends HashMap<K, V> {
    // 思路：降低（占用内存较多的）对象的引用级别,强引用-->弱引用

    // 分辨：（占用内存较多的）对象，作为泛型V存在
    // 将V转换成软应用的对象

    private HashMap<K, SoftValue<K, V>> temp;
    private ReferenceQueue<V> queue;// 装空袋子的队列

    public SoftReferenceMap() {
        // Object v = new Object();// （占用内存较多的）对象
        // SoftReference sr = new SoftReference(v);// 降低v的级别到软引用对象

        // 实现步骤：
        // ①将对象扔到软引用
        // ②GC回收软引用，扔到SoftReference，遍历SoftReference清理

        temp = new HashMap<K, SoftValue<K, V>>();
        queue = new ReferenceQueue<V>();
    }

    @Override
    public V put(K key, V value) {

        SoftValue<K, V> sr = new SoftValue<K, V>(key, value, queue);
        temp.put(key, sr);
        return null;
    }

    @Override
    public V get(Object key) {
        clearSR();
        // 如果此引用对象已经由程序或垃圾回收器清除，则此方法将返回 null。
        SoftValue<K, V> sr = temp.get(key);
        if (sr != null)
            return sr.get();
        return null;
    }

    @Override
    public boolean containsKey(Object key) {
        clearSR();

        SoftValue<K, V> sr = temp.get(key);
        if (sr != null) {
            V v = sr.get();
            /*
			 * if (v != null) { return true; } else { return false; }
			 */
            return v != null;
        }

        return false;
    }

    /**
     * 清理空袋子
     */
    @SuppressWarnings("unchecked")
    private void clearSR() {
        // 方案一：循环temp，检查软引用是否被回收
        // 存在问题：内存相对充足，GC不会回收，浪费资源，循环白做了
        // 方案二：利用GC，当GC在回收软引用的时候，扔到队列ReferenceQueue
        // 遍历ReferenceQueue，手动清理

        // 如果存在一个立即可用的对象，则从该队列中"移除"此对象并返回。
        // 否则此方法立即返回 null。
        SoftValue<K, V> poll = (SoftValue<K, V>) queue.poll();
        while (poll != null) {
            // 已经有软引用被回收了
            temp.remove(poll.key);
            poll = (SoftValue<K, V>) queue.poll();
        }
    }

    /**
     * 加强版软引用
     *
     * @param <K>   KEY
     * @param <V>   VALUE
     * @author Joker
     */
    @SuppressWarnings("hiding")
    private class SoftValue<K, V> extends SoftReference<V> {
        Object key;

        public SoftValue(K key, V r, ReferenceQueue<? super V> q) {
            super(r, q);
            this.key = key;
        }

    }

}
